package com.fly.visit.service;

import java.io.File;
import java.io.IOException;
import java.nio.charset.Charset;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.Random;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import javax.annotation.PostConstruct;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.RandomUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.core.io.ClassPathResource;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;
import org.springframework.util.ResourceUtils;

import com.fly.core.util.JsonBeanUtils;
import com.fly.core.util.ShellExecutor;
import com.fly.visit.entity.Article;
import com.fly.visit.entity.BlogData;
import com.fly.visit.entity.Record;
import com.fly.visit.service.init.DataInitor;

import lombok.extern.slf4j.Slf4j;

/***
 * web访问服务
 */
@Slf4j
@Service
public class WebVisitorSevice
{
    @Value("${init.account:qq_16127313}")
    private String userName;
    
    // 保存上次访问id
    long lastId;
    
    List<Article> articles = new ArrayList<>();
    
    @Autowired
    List<DataInitor> dataInitors;
    
    /**
     * lock零长度的byte数组对象创建起来将比任何对象都经济
     */
    private byte[] lock = new byte[0];
    
    /**
     * 通过web接口提交
     * 
     * @param articles
     */
    public void setArticles(List<Article> articles)
    {
        this.articles = articles.stream().collect(Collectors.toList());
        log.info("############## articles.size: {} ", articles.size());
    }
    
    public List<Article> getArticles()
    {
        return articles;
    }
    
    /**
     * 保存jsonData
     */
    private void saveJsonData()
    {
        try
        {
            if (ResourceUtils.isJarURL(new ClassPathResource("").getURL()))
            {
                log.info("will execute saveJsonData......");
                BlogData blogData = new BlogData().setData(new Record().setList(articles));
                FileUtils.writeStringToFile(new File("/data/data-all.json"), JsonBeanUtils.beanToJson(blogData, true), Charset.defaultCharset(), false);
            }
        }
        catch (IOException e)
        {
            log.error(e.getMessage(), e);
        }
    }
    
    @PostConstruct
    public void init()
    {
        addAccount(userName);
    }
    
    /**
     * 初始化账号数据
     * 
     * @param userName
     * @see [类、类#方法、类#成员]
     */
    private void addAccount(String userName)
    {
        synchronized (lock)
        {
            initArticles(userName);
            log.info("############## articles.size: {} ", articles.size());
            if (CollectionUtils.isEmpty(articles))
            {
                throw new RuntimeException("请先正确设置articles信息");
            }
            saveJsonData();
        }
    }
    
    /**
     * 新增
     * 
     * @param setUrl
     */
    public void addNew(Article article)
    {
        if (!articles.contains(article))
        {
            articles.add(0, article);
            saveJsonData();
        }
    }
    
    /**
     * 后台访问
     * 
     * @throws IOException
     * 
     */
    public void backVisit()
        throws IOException
    {
        synchronized (lock)
        {
            if (articles.isEmpty())
            {
                return;
            }
            // 对viewCount较小的执行访问，数据重复的话，最多生成5次
            int tryCount = 0;
            Article visit;
            do
            {
                visit = new Random().ints(3, 0, articles.size()).mapToObj(index -> articles.get(index)).min(Comparator.comparing(Article::getViewCount)).get();
            } while (visit.getArticleId() == lastId && tryCount++ < 5);
            
            if (visit.getArticleId() != lastId)
            {
                processVisited(tryCount, visit);
                lastId = visit.getArticleId();
            }
            else
            {
                log.info("process ignore ------------------------------------------------------------------------");
                lastId = 0L;
            }
        }
    }
    
    /**
     * 处理访问成功记录
     * 
     * @param tryCount
     * @param visit
     */
    private void processVisited(int tryCount, Article visit)
    {
        // 99%概率不执行访问[0,100)
        if (visit.getViewCount() > 20000 && RandomUtils.nextInt(0, 100) > 0)
        {
            log.info("process be ignore: {} ={} ==> {}", tryCount, StringUtils.leftPad(visit.getViewCount().toString(), 6), visit.getTitle());
            return;
        }
        String command = String.format("curl -e \"blog.csdn.net\"  \"%s\"", visit.getUrl());
        String titleLine = ShellExecutor.getTitleLine(command);
        if (StringUtils.isNotBlank(titleLine))
        {
            visit.addViewCount();
            log.info("process complted: {} ✈{} ==> {}", tryCount, StringUtils.leftPad(visit.getViewCount().toString(), 6), visit.getTitle());
        }
    }
    
    /**
     * 初始化articles
     * 
     * @param userName
     * @return
     * @throws IOException
     */
    private void initArticles(String userName)
    {
        // 串行流,有一个DataInitor执行init成功就返回
        articles.clear();
        dataInitors.stream()
            .peek(d -> log.info("{}", d.getClass().getName())) // debug
            .anyMatch(d -> d.init(userName, articles));
        
        AtomicInteger count = new AtomicInteger();
        articles.forEach(a -> log.info("{}. {} ==> {}", count.incrementAndGet(), a.getViewCount(), a.getTitle()));
    }
}
